package org.dromara.tearoom.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.tearoom.config.DouYinConfig;
import org.dromara.tearoom.domain.TerGroupBuy;
import org.dromara.tearoom.domain.bo.TerGroupBuyBo;
import org.dromara.tearoom.domain.enums.CouponPlateTypeEnum;
import org.dromara.tearoom.domain.enums.GroupBuyRelateTypeEnum;
import org.dromara.tearoom.domain.enums.GroupBuyStateEnum;
import org.dromara.tearoom.domain.vo.TerCouponTemplateVo;
import org.dromara.tearoom.domain.vo.TerGroupBuyVo;
import org.dromara.tearoom.service.ITerCouponService;
import org.dromara.tearoom.service.ITerCouponTemplateService;
import org.dromara.tearoom.service.ITerGroupBuyService;
import org.dromara.tearoom.service.IThirdGroupBuyService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 抖音服务
 *
 * @author CKJ
 * @description: 抖音服务
 * @date 2025/4/10 17:39
 */
@Slf4j
@Service("DY")
@RequiredArgsConstructor
public class DouYinServiceImpl implements IThirdGroupBuyService {

    private final RedisTemplate<String, String> redisTemplate;
    private final DouYinConfig douYinConfig;
    private final ITerGroupBuyService terGroupBuyService;
    private final ITerCouponService terCouponService;
    private final ITerCouponTemplateService terCouponTemplateService;


    /**
     * 获取有效AccessToken（自动刷新）
     */
    public String getValidAccessToken() {
        String token = redisTemplate.opsForValue().get(douYinConfig.getTokenKey());
        if (StringUtils.isEmpty(token)) {
            return refreshToken();
        }
        return token;
    }

    /**
     * 强制刷新token
     */
    @Synchronized
    public String refreshToken() {
        try {
            JSONObject loginBody = JSONUtil.createObj()
                .set("client_key", douYinConfig.getAppId())
                .set("client_secret", douYinConfig.getAppSecret())
                .set("grant_type", "client_credential");
            HttpResponse response = HttpRequest.post("https://open.douyin.com/oauth/client_token/")
                .header("Content-Type", "application/json")
                .body(loginBody.toString())
                .execute();
            if (response.isOk()) {
                JSONObject json = JSONUtil.parseObj(response.body());
                JSONObject data = JSONUtil.parseObj(json.getStr("data"));
                log.info("刷新AccessToken返回数据:{}", json);
                if (data.getInt("error_code") != 0) {
                    log.error(json.getStr("description"));
                    throw new ServiceException(json.getStr("description"));
                }
                String newToken = data.getStr("access_token");
                // 存储到Redis并设置小于2小时过期
                redisTemplate.opsForValue().set(
                    douYinConfig.getTokenKey(),
                    newToken,
                    100,
                    TimeUnit.MINUTES
                );
                log.info("AccessToken刷新成功:{}", newToken);
                return newToken;
            }
            throw new RuntimeException("登录失败: " + response.getStatus());
        } catch (Exception e) {
            log.error("AccessToken刷新异常", e);
            throw new RuntimeException("AccessToken刷新失败", e);
        }
    }


    /**
     * 验卷准备
     *
     * @param shortUrl
     * @return
     */
    @Override
    public List<TerGroupBuyVo> certificatePrepare(String shortUrl, String code, String poiId) {
        TerGroupBuyBo query = new TerGroupBuyBo();
        // 最多重试一次（初始尝试 + 一次刷新token后重试）
        for (int i = 0; i < 2; i++) {
            try {
                HttpRequest request = HttpUtil.createGet("https://open.douyin.com/goodlife/v1/fulfilment/certificate/prepare/");
                Map<String, String> headers = new HashMap<>();
                headers.put("Content-Type", "application/json");
                headers.put("access-token", getValidAccessToken());
                request.addHeaders(headers);
                log.info("Header参数:{}", headers);
                Map<String, Object> params = new HashMap<>();
                if (StrUtil.isBlank(poiId)) {
                    params.put("poi_id", douYinConfig.getPoiId());
                } else {
                    params.put("poi_id", poiId);
                }
                if (StrUtil.isNotBlank(code)) {
                    params.put("code", code);
                    query.setCode(code);
                }
                if (StrUtil.isNotBlank(shortUrl)) {
                    log.info("短链：{}", shortUrl);
                    String decodeStr = Base64.decodeStr(shortUrl);
                    log.info("解码短链：{}", decodeStr);
                    boolean isHttpUrl = ReUtil.isMatch("^https?://.*$", decodeStr);
                    if (!isHttpUrl) {
                        throw new ServiceException("二维码不正确!");
                    }
                    String longUrl = HttpUtil.get(decodeStr);
                    log.info("长链：{}", longUrl);
                    int beginIndex = longUrl.indexOf("object_id") + 10;
                    int endIndex = longUrl.indexOf("&", beginIndex);
                    String encrypted_data = longUrl.substring(beginIndex, endIndex);
                    log.info("encrypted_data：{}", encrypted_data);
                    params.put("encrypted_data", encrypted_data);
                    query.setCode(encrypted_data);
                }
                String str = request.form(params).timeout(1000).execute().body();
                log.info(str);
                List<TerGroupBuyBo> addList = new ArrayList<>();
                JSONObject json = JSONUtil.parseObj(str);
                JSONObject data = JSONUtil.parseObj(json.getStr("data"));
                // 检查错误码
                int errorCode = data.getInt("error_code");
                if (errorCode != 0) {
                    // 如果是access_token过期错误，并且是第一次尝试，则刷新token后重试
                    if (errorCode == 2190008 && i == 0) {
                        log.warn("access_token已过期，将刷新token后重试");
                        refreshToken(); // 强制刷新token
                        continue; // 重试
                    }
                    // 其他错误直接抛出异常
                    log.error(data.getStr("description"));
                    throw new ServiceException(data.getStr("description"));
                }
                List<JSONObject> certificates = JSONUtil.toList(data.getStr("certificates"), JSONObject.class);
                if (CollUtil.isEmpty(certificates)) {
                    throw new ServiceException("团购券验证失败！该券码已失效");
                }
                certificates.stream().forEach(item -> {
                    JSONObject sku = JSONUtil.parseObj(item.getStr("sku"));
                    TerCouponTemplateVo couponTemplateVo = terCouponTemplateService.queryByThirdPlateId(sku.getStr("sku_id"));
                    JSONObject amount = JSONUtil.parseObj(item.getStr("amount"));
                    TerGroupBuyBo bo = new TerGroupBuyBo();
                    bo.setUserId(LoginHelper.getAppLoginUser().getId());
                    bo.setName(sku.getStr("title"));
                    bo.setRelateId(couponTemplateVo.getId());
                    bo.setRelateType(GroupBuyRelateTypeEnum.COUPON.getType());
                    bo.setAccountId(sku.getStr("account_id"));
                    bo.setStatus(GroupBuyStateEnum.UN_USED.getState());
                    bo.setOriginalAmount(amount.getInt("coupon_pay_amount"));
                    bo.setPayAmount(amount.getInt("pay_amount"));
                    bo.setStartTime(DateUtil.date(item.getLong("start_time") * 1000));
                    bo.setExpireTime(DateUtil.date(item.getLong("expire_time") * 1000));
                    bo.setType(sku.getInt("groupon_type"));
                    bo.setPlateType(CouponPlateTypeEnum.DY.getType());
                    bo.setCreateTime(new Date());
                    bo.setCode(query.getCode());
                    bo.setEncryptedCode(item.getStr("encrypted_code"));
                    bo.setPlateOrderId(data.getStr("order_id"));
                    addList.add(bo);
                });
                terGroupBuyService.insertBatchByBo(addList);
                return terGroupBuyService.queryList(query);
            } catch (Exception e) {
                // 如果是第二次尝试仍然失败，则抛出异常
                if (i == 1) {
                    log.error("验证准备失败，已尝试刷新token", e);
                    throw e;
                }
                // 第一次尝试失败，继续循环重试
                log.error("验证准备失败，将尝试刷新token后重试", e);
            }
        }
        throw new ServiceException("验证准备失败，请稍后重试");
    }

    /**
     * 核销验卷
     *
     * @param encryptedCodes
     * @param poiId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void certificate(String encryptedCodes, String poiId, Long groupBuyId) {
        TerGroupBuy groupBuy = terGroupBuyService.queryByEncryptedCode(encryptedCodes);
        if (ObjectUtil.isEmpty(groupBuy)) {
            throw new ServiceException("优惠券不存在！");
        }
        if (GroupBuyStateEnum.isUsed(groupBuy.getStatus())) {
            throw new ServiceException("优惠券已核销！");
        }
        String uuid = IdUtil.simpleUUID();
        HttpRequest request = HttpUtil.createPost("https://open.douyin.com/goodlife/v1/fulfilment/certificate/verify/");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("access-token", getValidAccessToken());
        request.addHeaders(headers);
        log.info(headers.toString());
        JSONObject body = new JSONObject();
        body.put("verify_token", uuid);
        if (StrUtil.isBlank(poiId)) {
            body.put("poi_id", douYinConfig.getPoiId());
        } else {
            body.put("poi_id", poiId);
        }
        JSONArray array = new JSONArray();
        array.add(encryptedCodes);
        body.put("encrypted_codes", array);
        log.info(body.toString());
        String str = request.body(body.toString()).timeout(2000).execute().body();
        log.info(str);
        JSONObject json = JSONUtil.parseObj(str);
        JSONObject data = JSONUtil.parseObj(json.getStr("data"));
        if (data.getInt("error_code") != 0) {
            log.error(data.getStr("description"));
            throw new ServiceException(data.getStr("description"));
        }
        groupBuy.setStatus(GroupBuyStateEnum.USED.getState());
        groupBuy.setUseTime(new Date());
        terGroupBuyService.updateById(groupBuy);
        Set<Long> userIds = new HashSet<>();
        userIds.add(groupBuy.getUserId());
        terCouponService.takeCouponByGroupBy(groupBuy.getRelateId(), groupBuy.getId(), userIds, "抖音核销");
    }
}
